home *** CD-ROM | disk | FTP | other *** search
/ Amiga Collections: Panorama / Panorama - Disk 01 (1986-02-15)(Pacific North-West Amigas Club)[h AFL][b corrupt files].zip / Panorama - Disk 01 (1986-02-15)(Pacific North-West Amigas Club)[h AFL][b corrupt files].adf / iff.h < prev    next >
Text File  |  1989-10-24  |  21KB  |  422 lines

  1. #ifndef IFF_H
  2. #define IFF_H
  3. /*----------------------------------------------------------------------*/
  4. /* IFF.H  defs for IFF-85 Interchange Format Files.             10/8/85 */
  5. /*                                                                      */
  6. /* By Jerry Morrison and Steve Shaw, Electronic Arts.                   */
  7. /* This software is in the public domain.                               */
  8. /*----------------------------------------------------------------------*/
  9.  
  10. typedef LONG IFFP;      /* Status code result from an IFF procedure */
  11.         /* LONG, because must be type compatable with ID for GetChunkHdr.*/
  12.         /* Note that theot an IFF file.*/
  13. #define NO_FILE   -5    /* Tried to open file, DOS didn't find it.*/
  14. #define CLIENT_ERROR -6 /* Client made invalid request, for instance, asking
  15.                          * for more bytes than existed in chunk.*/
  16. #define BAD_FORM  -7    /* A client read proc complains about FORM semantics;
  17.                          * e.g. valid IFF, but missing a required chunk.*/
  18. #define SHORT_CHUNK -8  /* Client asked to IFFReadBytes more bytes than left
  19.                          * in the chunk. Could be client bug or bad form.*/
  20. #define BAD_IFF   -9    /* mal-formed IFF file. [TBD] Expand this into a
  21.                          * range of error codes.*/
  22. #define LAST_ERROR BAD_IFF
  23.  
  24. /* This MACRO is used to RETURN immediately when a termination condition is
  25.  * found. This is a pretty weird macro. It requires the caller to declare a
  26.  * local "IFFP iffp" and assign it. This wouldn't work as a subroutine since
  27.  * it returns for it's caller. */
  28. #define CheckIFFP()   { if (iffp != IFF_OKAY) return(iffp); }
  29.  
  30.  
  31. /* ---------- ID -------------------------------------------------------*/
  32.  
  33. typedef LONG ID;        /* An ID is four printable ASCII chars but
  34.                          * stored as a LONG for efficient copy & compare.*/
  35.  
  36. /* Four-character IDentifier builder.*/
  37. #define MakeID(a,b,c,d)  ( (a)<<24 | (b)<<16 | (c)<<8 | (d) )
  38.  
  39. /* Standard group IDs.  A chunk with one of these IDs contains a
  40.    SubTypeID followed by zero or more chunks.*/
  41. #define FORM MakeID('F','O','R','M')
  42. #define PROP MakeID('P','R','O','P')
  43. #define LIST MakeID('L','I','S','T')
  44. #define CAT  MakeID('C','A','T',' ')
  45. #define FILLER MakeID(' ',' ',' ',' ')
  46. /* The IDs "FOR1".."FOR9", "LIS1".."LIS9", & "CAT1".."CAT9" are reserved
  47.  * for future standardization.*/
  48.  
  49. /* Pseudo-ID used internally by chunk reader and writer.*/
  50. #define NULL_CHUNK 0L          /* No current chunk.*/
  51.  
  52.  
  53. /* ---------- Chunk ----------------------------------------------------*/
  54.  
  55. /* All chunks start with a type ID and a count of the data bytes that 
  56.    follow--the chunk's "logical size" or "data size". If that number is odd,
  57.    a 0 pad byte is written, too. */
  58. typedef struct {
  59.     ID    ckID;
  60.     LONG  ckSize;
  61.     } ChunkHeader;
  62.  
  63. typedef struct {
  64.     ID    ckID;
  65.     LONG  ckSize;
  66.     UBYTE ckData[ 1 /*REALLY: ckSize*/ ];
  67.     } Chunk;
  68.  
  69. /* Pass ckSize = szNotYetKnown to the writer to mean "compute the size".*/
  70. #define szNotYetKnown 0x80000001L
  71.  
  72. /* Need to know whether a value is odd so can word-align.*/
  73. #define IS_ODD(a)   ((a) & 1)
  74.  
  75. /* This macro rounds up to an even number. */
  76. #define WordAlign(size)   ((size+1)&~1)
  77.  
  78. /* ALL CHUNKS MUST BE PADDED TO EVEN NUMBER OF BYTES.
  79.  * ChunkPSize computes the total "physical size" of a padded chunk from
  80.  * its "data size" or "logical size". */
  81. #define ChunkPSize(dataSize)  (WordAlign(dataSize) + sizeof(ChunkHeader))
  82.  
  83. /* The Grouping chunks (LIST, FORM, PROP, & CAT) contain concatenations of
  84.  * chunks after a subtype ID that identifies the content chunks.
  85.  * "FORM type XXXX", "LIST of FORM type XXXX", "PROPerties associated
  86.  * with FORM type XXXX", or "conCATenation of XXXX".*/
  87. typedef struct {
  88.     ID    ckID;
  89.     LONG  ckSize;       /* this ckSize includes "grpSubID".*/
  90.     ID    grpSubID;
  91.     } GroupHeader;
  92.  
  93. typedef struct {
  94.     ID    ckID;
  95.     LONG  ckSize;
  96.     ID    grpSubID;
  97.     UBYTE grpData[ 1 /*REALLY: ckSize-sizeof(grpSubID)*/ ];
  98.     } GroupChunk;
  99.  
  100.  
  101. /* ---------- IFF Reader -----------------------------------------------*/
  102.  
  103. /******** Routines to support a stream-oriented IFF file reader *******
  104.  *
  105.  * These routines handle lots of details like error checking and skipping
  106.  * over padding. They're also careful not to read past any containing context.
  107.  *
  108.  * These routines ASSUME they're the only ones reading from the file.
  109.  * Client should check IFFP error codes. Don't press on after an error!
  110.  * These routines try to have no side effects in the error case, except
  111.  * partial I/O is sometimes unavoidable.
  112.  *
  113.  * All of these routines may return DOS_ERROR. In that case, ask DOS for the
  114.  * specific error code.
  115.  *
  116.  * The overall scheme for the low level chunk reader is to open a "group read
  117.  * context" with OpenRIFF or OpenRGroup, read the chunks with GetChunkHdr
  118.  * (and its kin) and IFFReadBytes, and close the context with CloseRGroup.
  119.  *
  120.  * The overall scheme for reading an IFF file is to use ReadIFF, ReadIList,
  121.  * and ReadICat to scan the file. See those procedures, ClientProc (below),
  122.  * and the skeleton IFF reader. */
  123.  
  124. /* Client passes ptrs to procedures of this type to ReadIFF which call them
  125.  * back to handle LISTs, FORMs, CATs, and PROPs.
  126.  *
  127.  * Use the GroupContext ptr when calling reader routines like GetChunkHdr.
  128.  * Look inside the GroupContext ptr for your ClientFrame ptr. You'll
  129.  * want to type cast it into a ptr to your containing struct to get your
  130.  * private contextual data (stacked property settings). See below. */
  131. typedef IFFP ClientProc(/* struct _GroupContext * */);
  132.  
  133. /* Client's context for reading an IFF file or a group.
  134.  * Client should actually make this the first component of a larger struct
  135.  * (it's personal stack "frame") that has a field to store each "interesting"
  136.  * property encountered.
  137.  * Either initialize each such field to a global default or keep a boolean
  138.  * indicating if you've read a property chunk into that field.
  139.  * Your getList and getForm procs should allocate a new "frame" and copy the
  140.  * parent frame's contents. The getProp procedure should store into the frame
  141.  * allocated by getList for the containing LIST. */
  142. typedef struct _ClientFrame {
  143.     ClientProc *getList, *getProp, *getForm, *getCat;
  144.     /* client's own data follows; place to stack property settings */
  145.     } ClientFrame;
  146.  
  147. /* Our context for reading a group chunk. */
  148. typedef struct _GroupContext {
  149.     struct _GroupContext *parent; /* Containing group; NULL => whole file. */
  150.     ClientFrame *clientFrame;     /* Reader data & client's context state. */
  151.     BPTR file;          /* Byte-stream file handle. */
  152.     LONG position;      /* The context's logical file position. */
  153.     LONG bound;         /* File-absolute context bound
  154.                          * or szNotYetKnown (writer only). */
  155.     ChunkHeader ckHdr;  /* Current chunk header. ckHdr.ckSize = szNotYetKnown
  156.                          * means we need to go back and set the size (writer only).
  157.                          * See also Pseudo-IDs, above. */
  158.     ID subtype;         /* Group's subtype ID when reading. */
  159.     LONG bytesSoFar;    /* # bytes read/written of current chunk's data. */
  160.     } GroupContext;
  161.  
  162. /* Computes the number of bytes not yet read from the current chunk, given
  163.  * a group read context gc. */
  164. #define ChunkMoreBytes(gc)  ((gc)->ckHdr.ckSize - (gc)->bytesSoFar)
  165.  
  166.  
  167. /***** Low Level IFF Chunk Reader *****/
  168.  
  169. /* Given an open file, open a read context spanning the whole file.
  170.  * This is normally only called by ReadIFF.
  171.  * This sets new->clientFrame = clientFrame.
  172.  * ASSUME context allocated by caller but not initialized.
  173.  * ASSUME caller doesn't deallocate the context before calling CloseRGroup.
  174.  * NOT_IFF ERROR if the file is too short for even a chunk header.*/
  175. extern IFFP OpenRIFF(/* BPTR, GroupContext *, ClientFrame * */);
  176.                      /* file, new,            clientFrame  */
  177.  
  178. /* Open the remainder of the current chunk as a group read context.
  179.  * This will be called just after the group's subtype ID has been read
  180.  * (automatically by GetChunkHdr for LIST, FORM, PROP, and CAT) so the
  181.  * remainder is a sequence of chunks.
  182.  * This sets new->clientFrame = parent->clientFrame. The caller should repoint
  183.  * it at a new clientFrame if opening a LIST context so it'll have a "stack
  184.  * frame" to store PROPs for the LIST. (It's usually convenient to also
  185.  * allocate a new Frame when you encounter FORM of the right type.)
  186.  *
  187.  * ASSUME new context allocated by caller but not initialized.
  188.  * ASSUME caller doesn't deallocate the context or access the parent context
  189.  * before calling CloseRGroup.
  190.  * BAD_IFF ERROR if context end is odd or extends past parent. */
  191. extern IFFP OpenRGroup(/* GroupContext *, GroupContext * */);
  192.                        /* parent,         new  */
  193.  
  194. /* Close a group read context, updating its parent context.
  195.  * After calling this, the old context may be deallocated and the parent
  196.  * context can be accessed again. It's okay to call this particular procedure
  197.  * after an error has occurred reading the group.
  198.  * This always returns IFF_OKAY. */
  199. extern IFFP CloseRGroup(/* GroupContext * */);
  200.                         /* old  */
  201.  
  202. /* Skip any remaining bytes of the previous chunk and any padding, then
  203.  * read the next chunk header into context.ckHdr.
  204.  * If the ckID is LIST, FORM, CAT, or PROP, this automatically reads the
  205.  * subtype ID into context->subtype.
  206.  * Caller should dispatch on ckID (and subtype) to an appropriate handler.
  207.  *
  208.  * RETURNS context.ckHdr.ckID (the ID of the new chunk header); END_MARK
  209.  * if there are no more chunks in this context; or NOT_IFF if the top level
  210.  * file chunk isn't a FORM, LIST, or CAT; or BAD_IFF if malformed chunk, e.g.
  211.  * ckSize is negative or too big for containing context, ckID isn't positive,
  212.  * or we hit end-of-file.
  213.  *
  214.  * See also GetFChunkHdr, GetF1ChunkHdr, and GetPChunkHdr, below.*/
  215. extern ID       GetChunkHdr(/* GroupContext * */);
  216.   /*  context.ckHdr.ckID       context  */
  217.  
  218. /* Read nBytes number of data bytes of current chunk. (Use OpenGroup, etc.
  219.  * instead to read the contents of a group chunk.) You can call this several
  220.  * times to read the data piecemeal.
  221.  * CLIENT_ERROR if nBytes < 0. SHORT_CHUNK if nBytes > ChunkMoreBytes(context)
  222.  * which could be due to a client bug or a chunk that's shorter than it
  223.  * ought to be (bad form). (on either CLIENT_ERROR or SHORT_CHUNK,
  224.  * IFFReadBytes won't read any bytes.) */
  225. extern IFFP IFFReadBytes(/* GroupContext *, BYTE *, LONG */);
  226.                          /* context,        buffer, nBytes  */
  227.  
  228.  
  229. /***** IFF File Reader *****/
  230.  
  231. /* This is a noop ClientProc that you can use for a getList, getForm, getProp,
  232.  * or getCat procedure that just skips the group. A simple reader might just
  233.  * implement getForm, store &ReadICat in the getCat field of clientFrame, and
  234.  * use &SkipGroup for the getList and getProp procs.*/
  235. extern IFFP SkipGroup(/* GroupContext * */);
  236.  
  237. /* IFF file reader.
  238.  * Given an open file, allocate a group context and use it to read the FORM,
  239.  * LIST, or CAT and it's contents. The idea is to parse the file's contents,
  240.  * and for each FORM, LIST, CAT, or PROP encountered, call the getForm,
  241.  * getList, getCat, or getProp procedure in clientFrame, passing the
  242.  * GroupContext ptr.
  243.  * This is achieved with the aid of ReadIList (which your getList should
  244.  * call) and ReadICat (which your getCat should call, if you don't just use
  245.  * &ReadICat for your getCat). If you want to handle FORMs, LISTs, and CATs
  246.  * nested within FORMs, the getForm procedure must dispatch to getForm,
  247.  * getList, and getCat (it can use GetF1ChunkHdr to make this easy).
  248.  *
  249.  * Normal return is IFF_OKAY (if whole file scanned) or IFF_DONE (if a client
  250.  * proc said "done" first).
  251.  * See the skeletal getList, getForm, getCat, and getProp procedures. */
  252. extern IFFP ReadIFF(/* BPTR, ClientFrame * */);
  253.                     /* file, clientFrame  */
  254.  
  255. /* IFF LIST reader.
  256.  * Your "getList" procedure should allocate a ClientFrame, copy the parent's
  257.  * ClientFrame, and then call this procedure to do all the work.
  258.  *
  259.  * Normal return is IFF_OKAY (if whole LIST scanned) or IFF_DONE (if a client
  260.  * proc said "done" first).
  261.  * BAD_IFF ERROR if a PROP appears after a non-PROP. */
  262. extern IFFP ReadIList(/* GroupContext *, ClientFrame * */);
  263.                       /* parent,         clientFrame  */
  264.  
  265. /* IFF CAT reader.
  266.  * Most clients can simply use this to read their CATs. If you must do extra
  267.  * setup work, put a ptr to your getCat procedure in the clientFrame, and
  268.  * have that procedure call ReadICat to do the detail work.
  269.  *
  270.  * Normal return is IFF_OKAY (if whole CAT scanned) or IFF_DONE (if a client
  271.  * proc said "done" first).
  272.  * BAD_IFF ERROR if a PROP appears in the CAT. */
  273. extern IFFP ReadICat(/* GroupContext * */);
  274.                      /* parent  */
  275.  
  276. /* Call GetFChunkHdr instead of GetChunkHdr to read each chunk inside a FORM.
  277.  * It just calls GetChunkHdr and returns BAD_IFF if it gets a PROP chunk. */
  278. extern ID       GetFChunkHdr(/* GroupContext * */);
  279.   /*  context.ckHdr.ckID        context  */
  280.  
  281. /* GetF1ChunkHdr is like GetFChunkHdr, but it automatically dispatches to the
  282.  * getForm, getList, and getCat procedure (and returns the result) if it
  283.  * encounters a FORM, LIST, or CAT. */
  284. extern ID       GetF1ChunkHdr(/* GroupContext * */);
  285.   /*  context.ckHdr.ckID         context  */
  286.  
  287. /* Call GetPChunkHdr instead of GetChunkHdr to read each chunk inside a PROP.
  288.  * It just calls GetChunkHdr and returns BAD_IFF if it gets a group chunk. */
  289. extern ID       GetPChunkHdr(/* GroupContext * */);
  290.   /*  context.ckHdr.ckID        context  */
  291.  
  292.  
  293. /* ---------- IFF Writer -----------------------------------------------*/
  294.  
  295. /******* Routines to support a stream-oriented IFF file writer *******
  296.  *
  297.  * These routines will random access back to set a chunk size value when the
  298.  * caller doesn't know it ahead of time. They'll also do things automatically
  299.  * like padding and error checking.
  300.  *
  301.  * These routines ASSUME they're the only ones writing to the file.
  302.  * Client should check IFFP error codes. Don't press on after an error!
  303.  * These routines try to have no side effects in the error case, except that
  304.  * partial I/O is sometimes unavoidable.
  305.  *
  306.  * All of these routines may return DOS_ERROR. In that case, ask DOS for the
  307.  * specific error code.
  308.  *
  309.  * The overall scheme is to open an output GroupContext via OpenWIFF or
  310.  * OpenWGroup, call either PutCk or {PutCkHdr {IFFWriteBytes}* PutCkEnd} for
  311.  * each chunk, then use CloseWGroup to close the GroupContext.
  312.  *
  313.  * To write a group (LIST, FORM, PROP, or CAT), call StartWGroup, write out
  314.  * its chunks, then call EndWGroup. StartWGroup automatically writes the
  315.  * group header and opens a nested context for writing the contents.
  316.  * EndWGroup closes the nested context and completes the group chunk. */
  317.  
  318.  
  319. /* Given a file open for output, open a write context.
  320.  * The "limit" arg imposes a fence or upper limit on the logical file
  321.  * position for writing data in this context. Pass in szNotYetKnown to be
  322.  * bounded only by disk capacity.
  323.  * ASSUME new context structure allocated by caller but not initialized.
  324.  * ASSUME caller doesn't deallocate the context before calling CloseWGroup.
  325.  * The caller is only allowed to write out one FORM, LIST, or CAT in this top
  326.  * level context (see StartWGroup and PutCkHdr).
  327.  * CLIENT_ERROR if limit is odd.*/
  328. extern IFFP OpenWIFF(/* BPTR, GroupContext *, LONG */);
  329.                      /* file, new,            limit {file position}  */
  330.  
  331. /* Start writing a group (presumably LIST, FORM, PROP, or CAT), opening a
  332.  * nested context. The groupSize includes all nested chunks + the subtype ID.
  333.  *
  334.  * The subtype of a LIST or CAT is a hint at the contents' FORM type(s). Pass
  335.  * in FILLER if it's a mixture of different kinds.
  336.  *
  337.  * This writes the chunk header via PutCkHdr, writes the subtype ID via
  338.  * IFFWriteBytes, and calls OpenWGroup. The caller may then write the nested
  339.  * chunks and finish by calling EndWGroup.
  340.  * The OpenWGroup call sets new->clientFrame = parent->clientFrame.
  341.  *
  342.  * ASSUME new context structure allocated by caller but not initialized.
  343.  * ASSUME caller doesn't deallocate the context or access the parent context
  344.  * before calling CloseWGroup.
  345.  * ERROR conditions: See PutCkHdr, IFFWriteBytes, OpenWGroup. */
  346. extern IFFP StartWGroup(/* GroupContext *, ID, LONG, ID, GroupContext * */);
  347.                         /* parent, groupType, groupSize, subtype, new  */
  348.  
  349. /* End a group started by StartWGroup.
  350.  * This just calls CloseWGroup and PutCkEnd.
  351.  * ERROR conditions: See CloseWGroup and PutCkEnd. */
  352. extern IFFP EndWGroup(/* GroupContext * */);
  353.                       /* old  */
  354.  
  355. /* Open the remainder of the current chunk as a group write context.
  356.  * This is normally only called by StartWGroup.
  357.  *
  358.  * Any fixed limit to this group chunk or a containing context will impose
  359.  * a limit on the new context.
  360.  * This will be called just after the group's subtype ID has been written
  361.  * so the remaining contents will be a sequence of chunks.
  362.  * This sets new->clientFrame = parent->clientFrame.
  363.  * ASSUME new context structure allocated by caller but not initialized.
  364.  * ASSUME caller doesn't deallocate the context or access the parent context
  365.  * before calling CloseWGroup.
  366.  * CLIENT_ERROR if context end is odd or PutCkHdr wasn't called first. */
  367. extern IFFP OpenWGroup(/* GroupContext *, GroupContext * */);
  368.                        /* parent,         new  */
  369.  
  370. /* Close a write context and update its parent context.
  371.  * This is normally only called by EndWGroup.
  372.  *
  373.  * If this is a top level context (created by OpenWIFF) we'll set the file's
  374.  * EOF (end of file) but won't close the file.
  375.  * After calling this, the old context may be deallocated and the parent
  376.  * context can be accessed again.
  377.  *
  378.  * Amiga DOS Note: There's no call to set the EOF. We just position to the
  379.  * desired end and return. Caller must Close file at that position.
  380.  * CLIENT_ERROR if PutCkEnd wasn't called first. */
  381. extern IFFP CloseWGroup(/* GroupContext * */);
  382.                         /* old  */
  383.  
  384. /* Write a whole chunk to a GroupContext. This writes a chunk header, ckSize
  385.  * data bytes, and (if needed) a pad byte. It also updates the GroupContext.
  386.  * CLIENT_ERROR if ckSize == szNotYetKnown. See also PutCkHdr errors. */
  387. extern IFFP PutCk(/* GroupContext *, ID,   LONG,   BYTE * */);
  388.                   /* context,        ckID, ckSize, *data  */
  389.  
  390. /* Write just a chunk header. Follow this will any number of calls to
  391.  * IFFWriteBytes and finish with PutCkEnd.
  392.  * If you don't yet know how big the chunk is, pass in ckSize = szNotYetKnown,
  393.  * then PutCkEnd will set the ckSize for you later.
  394.  * Otherwise, IFFWriteBytes and PutCkEnd will ensure that the specified
  395.  * number of bytes get written.
  396.  * CLIENT_ERROR if the chunk would overflow the GroupContext's bound, if
  397.  * PutCkHdr was previously called without a matching PutCkEnd, if ckSize < 0
  398.  * (except szNotYetKnown), if you're trying to write something other
  399.  * than one FORM, LIST, or CAT in a top level (file level) context, or
  400.  * if ckID <= 0 (these illegal ID values are used for error codes). */
  401. extern IFFP PutCkHdr(/* GroupContext *, ID,   LONG */);
  402.                      /* context,        ckID, ckSize  */
  403.  
  404. /* Write nBytes number of data bytes for the current chunk and update
  405.  * GroupContext.
  406.  * CLIENT_ERROR if this would overflow the GroupContext's limit or the
  407.  * current chunk's ckSize, or if PutCkHdr wasn't called first, or if
  408.  * nBytes < 0. */
  409. extern IFFP IFFWriteBytes(/* GroupContext *, BYTE *, LONG */);
  410.                           /* context,        *data,  nBytes  */
  411.  
  412. /* Complete the current chunk, write a pad byte if needed, and update
  413.  * GroupContext.
  414.  * If current chunk's ckSize = szNotYetKnown, this goes back and sets the
  415.  * ckSize in the file.
  416.  * CLIENT_ERROR if PutCkHdr wasn't called first, or if client hasn't
  417.  * written 'ckSize' number of bytes with IFFWriteBytes. */
  418. extern IFFP PutCkEnd(/* GroupContext * */);
  419.                      /* context  */
  420.  
  421. #endif
  422.